/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package au.edu.swin.bank.ejb.session.stateless;

import au.edu.swin.bank.persistence.entity.Account;
import au.edu.swin.bank.persistence.entity.Archive;
import au.edu.swin.bank.persistence.entity.BankTransaction;
import au.edu.swin.bank.persistence.entity.BankTransactionDTO;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import javax.ejb.Remote;
import javax.ejb.Local;
import javax.ejb.Stateless;
import javax.persistence.Query;
import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.PersistenceContext;

/**
 *
 * @author scott
 */
@Stateless
@Local(AccountLocal.class)
@Remote(AccountRemote.class)
public class AccountBean implements AccountRemote, AccountLocal
{
    @PersistenceContext
    private EntityManager em;

    /**
     * Gets the balance of an account with the specified ID.
     *
     * @param accountId
     *
     * @return the balance of the account
     */
    public double getBalance(BigInteger accountId)
    {
        Account account = getAccount(accountId);
        return account.getBalance();
    }

    /**
     * Performs a withdrawal of a specified amount on a specified account.
     * @param amount The amount to withdraw
     * @param account The account to withdraw from
     * @return True if success, otherwise false
     */
    public boolean withdraw(double amount, Account account)
    {
        double balance = account.getBalance();

        //Check if the account has enough balance to cover the withdrawal
        if (amount >= 0 && balance >= amount)
        {
            account.setBalance(balance - amount);
            em.persist(account);
            return true;
        }
        else
        {
            return false;
        }
    }

    /**
     * Performs a deposit of a specified amount on a specified account.
     * @param amount The amount to deposit
     * @param account The account to deposit into
     * @return Returns true once the deposit has been processed
     */
    public boolean deposit(double amount, Account account)
    {
        if (amount >= 0)
        {
            account.setBalance(account.getBalance() + amount);
            em.persist(account);
            return true;
        }
        else
        {
            return false;
        }
    }

    /**
     * Retrieves an account with the specified ID.
     * @param accountId The ID of the account to retrieve
     * @return The Account object
     */
    public Account getAccount(BigInteger accountId)
    {
        try {
            Query query = em.createNamedQuery("Account.findByAccountid").setParameter("accountid", accountId);
            Account account = (Account)query.getSingleResult();
            return account;
        }
        catch (NoResultException e) {
            return null;
        }
    }

    /**
     * Gets a list of transactions for the specified account between the specified dates
     * @param aAccount The account to get the transactions for
     * @param aStartDate The start date of the transaction period
     * @param aEndDate The end date of the transaction period
     * @return A collection of transactions
     */
    public Collection<BankTransactionDTO> getTransactions(Account aAccount, BigInteger aStartDate, BigInteger aEndDate)
    {
        List<BankTransactionDTO> transactionDTOs = new ArrayList<BankTransactionDTO>();
        BigInteger accountId = aAccount.getAccountId();

        try {
            em.getEntityManagerFactory().getCache().evictAll();

            //Find all transactions
            Query query = em.createNamedQuery("BankTransaction.findByFrmaccountid")
                    .setParameter("frmaccountid", accountId)
                    .setParameter("startdate", aStartDate)
                    .setParameter("enddate", aEndDate);
            Collection<BankTransaction> transactions = query.getResultList();
            Iterator iterator = transactions.iterator();

            //Add them all to a list and return the list
            while(iterator.hasNext()) {
                BankTransaction bt = (BankTransaction)iterator.next();
                transactionDTOs.add(new BankTransactionDTO(bt));
            }

            query = em.createNamedQuery("BankTransaction.findByRecaccountid")
                    .setParameter("recaccountid", accountId)
                    .setParameter("startdate", aStartDate)
                    .setParameter("enddate", aEndDate);
            transactions = query.getResultList();
            iterator = transactions.iterator();

            //Add them all to a list and return the list
            while(iterator.hasNext()) {
                BankTransaction bt = (BankTransaction)iterator.next();
                transactionDTOs.add(new BankTransactionDTO(bt));
            }

            //BankTransactionDTO implements Comparable so it should be able
            //to be sorted like this
            Collections.sort(transactionDTOs);
        } catch (NoResultException e) {
            return transactionDTOs;
        }
        return transactionDTOs;
    }

    /**
     * Persists the object to the database.
     * @param object
     */
    public void persist(Object object)
    {
        em.persist(object);
    }

    /**
     * Archives the balance of all accounts held by SwinBank.
     * Records the account number, balance and current time.
     */
    public void archive()
    {
        Query query = em.createNamedQuery("Account.findAll");
        List<Account> list = query.getResultList();
        BigInteger date = BigInteger.valueOf(System.currentTimeMillis());
        for(Account a : list){
            Archive toPersist = new Archive();
            toPersist.setAccount(a);
            toPersist.setBalance(a.getBalance());
            toPersist.setDate(date);
            persist(toPersist);
        }
    }

    /**
     * Forces an eviction of the data in the Bean's cache.
     */
    public void forceEvict(){
        em.getEntityManagerFactory().getCache().evictAll();
    }
}
